#ifndef _M_BINDING_H_
#define _M_BINDING_H_

#include <iostream>
#include <google/protobuf/map.h>
#include <string>
#include <vector>
#include <cassert>
#include <sstream>
#include <functional>
#include <mutex>
#include <unordered_map>
#include <string>

#include "../mqcommon/mqProto.pb.h"
#include "../mqcommon/logger.hpp"
#include "../mqcommon/helper.hpp"

namespace MyRabbitMQ
{
    class Binding
    {
    public:
        using ptr = std::shared_ptr<Binding>;

        Binding() {}
        Binding(const Binding &) = delete;
        Binding(Binding &&) = default;
        Binding &operator=(const Binding &) = delete;

        Binding(const std::string &exchangeName, const std::string &msgQueueName, const std::string &bindingKey)
            : m_exchangeName(exchangeName), m_msgQueueName(msgQueueName), m_bindingKey(bindingKey)
        {
        }

    public:
        std::string m_exchangeName;
        std::string m_msgQueueName;
        std::string m_bindingKey;
    };

    //绑定信息管理单元, 队列为单位
    using msgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
    //绑定信息管理单元, 交换机为单位
    using BindingMap = std::unordered_map<std::string, msgQueueBindingMap>;

    class BindingMapper
    {
    public:
        using ptr = std::shared_ptr<BindingMapper>;

        BindingMapper(const std::string& dbfilename)
        :m_sqlite(dbfilename)
        {
            //创建父目录
            std::string parentDir = FileHelper::parentDirectory(dbfilename);
            FileHelper::createDirectory(parentDir);

            //打开数据库文件,如果不存在则断言
            assert(m_sqlite.open());

            //创建表
            this->createTable();
        }

        //创建表
        bool createTable()
        {
            std::string sql = "create table if not exists binding_table (\
            exchangeName varchar(32), msgQueueName varchar(32),bindingKey varchar(128));";

            bool ret = m_sqlite.exec(sql,nullptr,nullptr);
            
            //如果表都创建失败了,那就不要再继续进行下去了
            if(!ret)
            {
                LOG(logLevel::ERROR) << "创建绑定数据表失败! 程序即将退出" << std::endl;
                //直接异常退出程序
                abort();
            }

            return true;
        }

        //删除表
        bool removeTable()
        {
            std::string sql = "drop table if exists binding_table;";
            
            bool ret = m_sqlite.exec(sql,nullptr,nullptr);

            if(!ret)
            {
                LOG(logLevel::ERROR) << "删除绑定数据表失败! 程序即将退出" << std::endl;
                //直接异常退出程序
                abort();
            }
            return true;
        }

        //新增绑定
        bool insert(const Binding::ptr& ptr)
        {
            std::stringstream ss;

            ss << "insert into binding_table values(";
            ss << "'" << ptr->m_exchangeName << "',"; 
            ss << "'" << ptr->m_msgQueueName << "',"; 
            ss << "'" << ptr->m_bindingKey << "');"; 
    
            return m_sqlite.exec(ss.str(),nullptr,nullptr);
        }

        //删除绑定
        bool remove(const std::string& ename,const std::string& qname)
        {
            std::stringstream ss;
            ss << "delete from binding_table where exchangeName = '" <<  ename << "' and ";
            ss << "msgQueueName = '" << qname << "';"; 
            return m_sqlite.exec(ss.str(),nullptr,nullptr);       
        }                                                         

        //删除所有交换机对应的绑定信息
        bool removeExchangeBindings(const std::string& ename)
        {
            std::stringstream ss;
            ss << "delete from binding_table where exchangeName = '" <<  ename << "';";
            return m_sqlite.exec(ss.str(),nullptr,nullptr);       
        }

        //删除所有队列对应的绑定信息
        bool removeMsgQueueBindings(const std::string& qname)
        {
            std::stringstream ss;
            ss << "delete from binding_table where msgQueueName = '" <<  qname << "';";
            return m_sqlite.exec(ss.str(),nullptr,nullptr);       
        }

        //恢复历史数据                                       
        BindingMap recovery()                              
        {                                                         
            BindingMap result;                                   
            std::stringstream ss;                                 
            ss << "select * from binding_table;";                
            m_sqlite.exec(ss.str(),recoverCallback,&result);
            return result;                                         
        }

    private:
        SqliteHelper m_sqlite;

        //存入位置, 一行数据的列数, 一行数据的字符指针数组, 每列字段名称
        static int recoverCallback(void *args, int row, char ** dataOfCol, char **rowOfName)
        {
            BindingMap* result = reinterpret_cast<BindingMap*>(args);
            Binding::ptr bp = std::make_shared<Binding>(dataOfCol[0],dataOfCol[1],dataOfCol[2]);
            
            msgQueueBindingMap& qmap = (*result)[bp->m_exchangeName];
            qmap.insert(std::make_pair(bp->m_msgQueueName,bp));
            return 0;
        }

    };
    
    //绑定数据内存层面管理类
    class BindingManager
    {
    public:
        using ptr = std::shared_ptr<BindingManager>;
        BindingManager(const std::string& dbfilename)
        :m_mapper(dbfilename)
        {
            m_bindings = m_mapper.recovery();
        }

        //声明绑定
        bool declareBinding(const std::string& ename, const std::string& qname, 
        const std::string& bindingKey, bool isDurable)
        {
            //加锁
            std::unique_lock<std::mutex>(m_mtx);

            //判断绑定信息是否已经存在
            auto it = m_bindings.find(ename);

            //首先判断队列管理单元是否存在, 如果存在,再判断队列管理单元内是否有相应绑定数据
            if(it != m_bindings.end() && (it->second.find(qname) != it->second.end()))
            {
                //已经存在, 返回
                return true;
            }

            //绑定数据不存在, 进行添加逻辑
            auto ptr = std::make_shared<Binding>(ename,qname,bindingKey);

            //如果设置持久化标志, 需要添加到数据库中, 由于该操作可能失败, 故放在内存层面添加之前
            if(isDurable)
            {
                bool ret = m_mapper.insert(ptr);                        
                if(!ret)
                {
                    LOG(logLevel::ERROR) << "在声明绑定试图进行持久化时出现问题" << std::endl; 
                    return false;
                }
            }

            //添加到内存中
            m_bindings[ename].insert(std::make_pair(qname,ptr));
            return true;
        }

        //删除绑定
        bool deleteBinding(const std::string& ename,const std::string& qname)
        {
            std::unique_lock<std::mutex>(m_mtx);

            auto it = m_bindings.find(ename);

            if(it == m_bindings.end() || it->second.find(qname) == it->second.end())
            {
                return true;
            }

            //我们这里选择默认删除持久化数据, 因为:如果数据就是持久化的, 那么正好, 如果数据
            //非持久化, 那么删了也不影响
            m_mapper.remove(ename,qname);
            m_bindings[ename].erase(qname);   
            
            return true;
        }

        //删除指定交换机的所有绑定信息
        bool deleteExchangeBindings(const std::string& ename)
        {
            std::unique_lock<std::mutex>(m_mtx);

            auto it = m_bindings.find(ename);

            if(it == m_bindings.end())
            {
                return true;                
            }

            m_mapper.removeExchangeBindings(ename);
            m_bindings.erase(ename);
            return true;            
        }

        //删除指定队列的所有绑定信息
        bool deleteMsgQueueBindings(const std::string& qname)
        {
            std::unique_lock<std::mutex>(m_mtx);

            for(auto& f:m_bindings)
            {
                f.second.erase(qname);
            }

            m_mapper.removeMsgQueueBindings(qname);
            return true;
        }

        //获取指定交换机的所有绑定信息
        msgQueueBindingMap getExchangeBindings(const std::string& ename)
        {
            std::unique_lock<std::mutex>(m_mtx);

            auto it = m_bindings.find(ename);
            if(it == m_bindings.end())
            {
                return msgQueueBindingMap();
            }

            return it->second;            
        }

        //获取指定绑定信息
        Binding::ptr getBinding(const std::string& ename, const std::string& qname)
        {
            std::unique_lock<std::mutex>(m_mtx);
            
            auto eit = m_bindings.find(ename);
            if(eit == m_bindings.end())
            {
                return Binding::ptr();
            }

            auto qit = eit->second.find(qname);
            if(qit == eit->second.end())
            {
                return Binding::ptr();
            }

            return qit->second;   
        }

        //获取绑定信息数量
        size_t size()
        {
            std::unique_lock<std::mutex>(m_mtx);
            size_t count = 0;
            for(auto& f : m_bindings)
            {
                count += f.second.size();
            }

            return count;
        }

        //销毁所有绑定数据
        void clear()
        {
            std::unique_lock<std::mutex>(m_mtx);

            m_mapper.removeTable();

            for(auto& f : m_bindings)
            {
                f.second.clear();
            }
         
            m_bindings.clear();
        }

        //判断指定绑定是否存在
        bool exists(const std::string& ename, const std::string& qname)
        {
            std::unique_lock<std::mutex>(m_mtx);
            
            auto eit = m_bindings.find(ename);
            if(eit == m_bindings.end())
            {
                return false;
            }

            auto qit = eit->second.find(qname);
            if(qit == eit->second.end())
            {
                return false;
            }

            return true;
        }
    private:
        std::mutex m_mtx;
        BindingMapper m_mapper;
        BindingMap m_bindings;
    };
};
#endif
